package com.wyd.common.sm.test;

import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;

/**
 * 国密sm4对称加密
 */
@Slf4j
public class SM4Utils {
    public static final String ALGORIGTHM_NAME = "SM4";
    public static final String ALGORITHM_NAME_ECB = "SM4/ECB/PKCS7Padding";
//    public static final String ALGORITHM_NAME_ECB = "SM4/ECB/PKCS5Padding";
    public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding";
    public static final int DEFAULT_KEY_SIZE = 128;

    private SM4Utils() {
    }

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 自动生成密钥
     */
    public static byte[] generateKey() throws Exception {
        return generateKey(DEFAULT_KEY_SIZE);
    }

    /**
     * 生成密钥十六进制编码
     */
    public static String generateKeyToHexString() throws Exception {
        return ByteUtils.toHexString(generateKey(DEFAULT_KEY_SIZE));
    }

    public static byte[] generateKey(int keySize) throws Exception {
        KeyGenerator kg = KeyGenerator.getInstance(ALGORIGTHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
        kg.init(keySize, new SecureRandom());
        return kg.generateKey().getEncoded();
    }

    /**
     * 加密
     *
     * @param hexKey 十六进制编码密钥（也作为偏移量使用）
     * @param param  待加密字符串
     * @return 十六进制编码密文
     * @throws Exception
     */
    public static String encryptCbcPadding(String hexKey, String param) throws Exception {
        byte[] data = param.getBytes(StandardCharsets.UTF_8);

        return encryptCbcPadding(hexKey, data);
    }

    /**
     * 加密
     *
     * @param hexKey 十六进制编码密钥（也作为偏移量使用）
     * @param data  待加密字节
     * @return 十六进制编码密文
     * @throws Exception
     */
    public static String encryptCbcPadding(String hexKey, byte[] data) throws Exception {
        byte[] key = ByteUtils.fromHexString(hexKey);

        Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_CBC_PADDING, BouncyCastleProvider.PROVIDER_NAME);
        Key sm4Key = new SecretKeySpec(key, ALGORIGTHM_NAME);

        cipher.init(Cipher.ENCRYPT_MODE, sm4Key, new IvParameterSpec(key));
        final long l = System.currentTimeMillis();
        byte[] bs = cipher.doFinal(data);
        final long l1 = System.currentTimeMillis();

        final String s = ByteUtils.toHexString(bs);

        final long l2 = System.currentTimeMillis();

        System.out.println(l1 - l);
        System.out.println(l2 - l1);

        return s;
    }

    /**
     * 加密
     *
     * @param hexKey 十六进制编码密钥（也作为偏移量使用）
     * @param data  待加密字节
     * @return 十六进制编码密文
     * @throws Exception
     */
    public static String encryptEcb(String hexKey, byte[] data) throws Exception {
        byte[] key = ByteUtils.fromHexString(hexKey);

        Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB, BouncyCastleProvider.PROVIDER_NAME);
        Key sm4Key = new SecretKeySpec(key, ALGORIGTHM_NAME);

        cipher.init(Cipher.ENCRYPT_MODE, sm4Key);
        byte[] bs = cipher.doFinal(data);

        return ByteUtils.toHexString(bs);
    }

    /**
     * 解密
     *
     * @param hexKey  十六进制编码密钥（也作为偏移量使用）
     * @param hexData 十六进制编码密文
     * @return
     * @throws Exception
     */
    public static String decryptCbcPadding(String hexKey, String hexData) throws Exception {
        byte[] key = ByteUtils.fromHexString(hexKey);
        byte[] data = ByteUtils.fromHexString(hexData);

        Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_CBC_PADDING, BouncyCastleProvider.PROVIDER_NAME);
        Key sm4Key = new SecretKeySpec(key, ALGORIGTHM_NAME);
        cipher.init(Cipher.DECRYPT_MODE, sm4Key, new IvParameterSpec(key));
        byte[] bs = cipher.doFinal(data);

        return new String(bs);
    }

    /**
     * 解密
     *
     * @param hexKey  十六进制编码密钥（也作为偏移量使用）
     * @param hexData 十六进制编码密文
     * @return
     * @throws Exception
     */
    public static String decryptEcb(String hexKey, String hexData) throws Exception {
        byte[] key = ByteUtils.fromHexString(hexKey);
        byte[] data = ByteUtils.fromHexString(hexData);

        Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB, BouncyCastleProvider.PROVIDER_NAME);
        Key sm4Key = new SecretKeySpec(key, ALGORIGTHM_NAME);
        cipher.init(Cipher.DECRYPT_MODE, sm4Key);
        byte[] bs = cipher.doFinal(data);

        return new String(bs);
    }
}
